package crowdedroom.droidsimplelogger;

import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;

import android.content.Context;
import android.util.Log;

/**
 * The Class DroidSimpleLogger.
 */
public class DroidSimpleLogger {
  /**
   * The Interface AndroidLogType.
   */
  public interface AndroidLogType {
    /**
     * Log.
     * @param tag the tag
     * @param message the message
     * @param t the t
     */
    void log(String tag, String message, Throwable t);
    /**
     * The Constant VERBOSE.
     */
    public static final AndroidLogType VERBOSE =
      new AndroidLogType() {
        public void log(String tag, String message, Throwable t) {
          Log.v(tag, message, t);
        }
      };
    /**
     * The Constant DEBUG.
     */
    public static final AndroidLogType DEBUG = new AndroidLogType() {
      public void log(String tag, String message, Throwable t) {
        Log.d(tag, message, t);
      }
    };
    /**
     * The Constant INFO.
     */
    public static final AndroidLogType INFO = new AndroidLogType() {
      public void log(String tag, String message, Throwable t) {
        Log.i(tag, message, t);
      }
    };
    /**
     * The Constant WARN.
     */
    public static final AndroidLogType WARN = new AndroidLogType() {
      public void log(String tag, String message, Throwable t) {
        Log.w(tag, message, t);
      }
    };
    /**
     * The Constant ERROR.
     */
    public static final AndroidLogType ERROR = new AndroidLogType() {
      public void log(String tag, String message, Throwable t) {
        Log.e(tag, message, t);
      }
    };
  }
  /**
   * The Interface ILogDecorator.
   */
  public interface ILogDecorator {
    /**
     * Log.
     * @param tag the tag
     * @param message the message
     * @param t the t
     */
    void log(String tag, String message, Throwable t);
  }
  /**
   * The Constant FILE.
   */
  public static final String FILE = "log.log";
  /**
   * The decorators.
   */
  private static CopyOnWriteArrayList<WeakReference<ILogDecorator>> decorators =
    new CopyOnWriteArrayList<WeakReference<ILogDecorator>>();
  /**
   * The log to file.
   */
  private static boolean logToFile = false;
  /**
   * The log to log cat.
   */
  private static boolean logToLogCat = true;
  /**
   * The log to decorators.
   */
  private static boolean logToDecorators = true;
  /**
   * Clear.
   * @param context the context
   */
  public static void clear(final Context context) {
    if (context == null) {
      return;
    }
    try {
      context.deleteFile(FILE);
    } catch (final Exception e) {
      // swallow
    }
  }
  /**
   * Read.
   * @param context the context
   * @return the string
   */
  public static String read(final Context context) {
    if (context == null) {
      return "";
    }
    BufferedInputStream input = null;
    final StringBuilder sb = new StringBuilder();
    try {
      input = new BufferedInputStream(context.openFileInput(FILE));
      int line;
      while ((line = input.read()) > 0) {
        sb.append((char) line);
      }
    } catch (final Exception e) {
      // Swallow
    } finally {
      if (input != null) {
        try {
          input.close();
        } catch (final Exception ignored) {
          // Swallow
        }
      }
    }
    return sb.toString();
  }
  /**
   * Log to android.
   * @param type the type
   * @param tag the tag
   * @param message the message
   * @param t the t
   */
  public static void logToAndroid(
    final AndroidLogType type,
    final String tag,
    final String message,
    final Throwable t) {
    if (type != null && logToLogCat) {
      type.log(tag, message, t);
    }
  }
  /**
   * Log to file.
   * @param context the context
   * @param tag the tag
   * @param message the message
   * @param t the t
   * @param type the type
   * @param logToDecorators the log to decorators
   */
  public static void logToFile(
    final Context context,
    final String tag,
    final String message,
    final Throwable t,
    final AndroidLogType type,
    final boolean logToDecorators) {
    if (!logToFile) {
      return;
    }
    if (type != null) {
      type.log(tag, message, t);
    }
    if (logToDecorators) {
      logToDecorators(tag, message, t);
    }
    FileOutputStream output = null;
    int mode = Context.MODE_APPEND;
    try {
      output = context.openFileOutput(FILE, mode);
      if (tag != null) {
        output.write(tag.getBytes());
        output.write(": ".getBytes());
      }
      if (message != null) {
        output.write(message.getBytes());
        output.write("\n".getBytes());
      }
      if (t != null) {
        output.write(serializeThrowable(t).getBytes());
        output.write("\n\n".getBytes());
      }
    } catch (final Exception e) {
      // Swallow
    } finally {
      try {
        if (output != null) {
          output.close();
        }
      } catch (final Exception e) {
        // Swallow
      }
    }
  }
  /**
   * Log to file.
   * @param context the context
   * @param tag the tag
   * @param message the message
   */
  public static void logToFile(
    Context context,
    final String tag,
    final String message) {
    logToFile(context, tag, message, null, null, false);
  }
  /**
   * Serialize throwable.
   * @param t the t
   * @return the string
   * @throws IOException Signals that an I/O exception has occurred.
   */
  private static String serializeThrowable(Throwable t)
    throws IOException {
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);
    try {
      t.printStackTrace(pw);
      return sw.toString();
    } finally {
      sw.close();
      pw.close();
    }
  }
  /**
   * Log to decorators.
   * @param tag the tag
   * @param message the message
   * @param t the t
   */
  public static void logToDecorators(
    final String tag,
    final String message,
    final Throwable t) {
    if (!logToDecorators) {
      return;
    }
    Iterator<WeakReference<ILogDecorator>> it = decorators.iterator();
    while (it.hasNext()) {
      if (it.next().get() != null) {
        it.next().get().log(tag, message, t);
      } else {
        it.remove();
      }
    }
  }
  /**
   * Register decorator.
   * @param decorator the decorator
   */
  public static synchronized void registerDecorator(
    ILogDecorator decorator) {
    decorators.add(new WeakReference<ILogDecorator>(decorator));
  }
  /**
   * Removes the decorator.
   * @param decorator the decorator
   */
  public static synchronized void removeDecorator(
    ILogDecorator decorator) {
    for (int i = 0; i < decorators.size(); i++) {
      if (decorators.get(i).get() == decorator) {
        decorators.remove(i);
      }
    }
  }
  /**
   * Sets the log to log cat.
   * @param logToLogCat the new log to log cat
   */
  public static void setLogToLogCat(boolean logToLogCat) {
    DroidSimpleLogger.logToLogCat = logToLogCat;
  }
  /**
   * Checks if is log to log cat.
   * @return true, if is log to log cat
   */
  public static boolean isLogToLogCat() {
    return logToLogCat;
  }
  /**
   * Sets the log to decorators.
   * @param logToDecorators the new log to decorators
   */
  public static void setLogToDecorators(boolean logToDecorators) {
    DroidSimpleLogger.logToDecorators = logToDecorators;
  }
  /**
   * Checks if is log to decorators.
   * @return true, if is log to decorators
   */
  public static boolean isLogToDecorators() {
    return logToDecorators;
  }
  /**
   * Sets the log to file.
   * @param logToFile the new log to file
   */
  public static void setLogToFile(boolean logToFile) {
    DroidSimpleLogger.logToFile = logToFile;
  }
  /**
   * Checks if is log to file.
   * @return true, if is log to file
   */
  public static boolean isLogToFile() {
    return logToFile;
  }
}
